using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{

    public class PipelineStage : Component
    {

        public PipelineBus InputBus;
        public PipelineBus OutputBus;

        public NodeVector OutputStall;
        public NodeVector InputStall;
        public NodeVector OutputDataReady;
        public NodeVector InputDataReady;
        public NodeVector OutputEnable;

        public NodeVector InputData;
        public NodeVector OutputData;

        protected ClockComponent oClock;
        protected NodeVector nvReset;

        public PipelineStage(Component poParentComponent, string psName, ClockComponent poClock,NodeVector pnvReset, int piWidth)
            : base(poParentComponent, psName)
        {
            oClock = poClock;
            nvReset = pnvReset;
            OutputStall = this.CreateNodeVector("STALL_O", 1);
            InputStall = this.CreateNodeVector("STALL_I", 1);
            OutputDataReady = this.CreateNodeVector("DATA_READY_O", 1);
            InputDataReady = this.CreateNodeVector("DATA_READY_I", 1);
            OutputEnable = this.CreateNodeVector("ENABLE_O", 1);
            InputData = this.CreateNodeVector("DATA_I", piWidth);
            OutputData = this.CreateNodeVector("DATA_O", piWidth);
            //GenerateRegisters(piWidth);
            OutputBus = new PipelineBus(this, "in_bus", piWidth);
            InputBus = new PipelineBus(this, "out_bus", piWidth);
            OutputBus.Stall.Join(InputStall);
            OutputBus.DataReady.Join(OutputDataReady);
            OutputBus.Data.Join(OutputData);
            InputBus.Stall.Join(OutputStall);
            InputBus.DataReady.Join(InputDataReady);
            InputBus.Data.Join(InputData);
        }

        private void GenerateRegisters(int piWidth)
        {
        }

        public override void GenerateStructure()
        {
            int iWidth = InputData.Nodes.Count;
            ResetReg oRegData = new ResetReg(this, "data_reg", oClock, iWidth, true, true, false);
            ResetReg oRegDataReady = new ResetReg(this, "reg_data_ready", oClock, 1, true, true, false);

            oRegData.ResetIn.Join(nvReset);
            oRegDataReady.ResetIn.Join(nvReset);

            oRegData.ResetTo.Join(0);
            oRegDataReady.ResetTo.Join(0);

            oRegDataReady.InputNodes.Join(InputDataReady);
            oRegDataReady.OutputNodes.Join(OutputDataReady);

            oRegData.InputNodes.Join(InputData);
            oRegData.OutputNodes.Join(OutputData);

            oRegDataReady.EnableNodes.Join(OutputEnable);
            oRegData.EnableNodes.Join(OutputEnable);

            OutputStall.Join(OutputDataReady & InputStall);
            OutputEnable.Join(!OutputStall);
        }
    }
}
